﻿using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OverStore.Configuration;
using OverStore.Configuration.Builder.Fluent;
using OverStore.Configuration.Components;
using OverStore.Runtime.Storage;
using OverStore.Runtime.PersistenceServiceProvider;
using OverStore.Configuration.Builder;
using OverStore.Sql.TableSchema;
using OverStore.Runtime.ObjectRepository.CommandSet;
using OverStore.Configuration.ObjectRepository.Depot;

namespace OverStore.TutorialAsTest.Application_1._ObjectReader_performance
{
    [TestClass]
    public class UnitOfWorkPerformanceTest
    {
        class Deal
        {
            public Guid Id { get; set; }

            public string Description { get; set; }

            public DateTime Date { get; set; }

            public Guid Item { get; set; }

            public decimal Quantity { get; set; }

            public decimal Cost { get; set; }

            public Guid SourcePlace { get; set; }

            public Guid TargetPlace { get; set; }

            public Guid Actor { get; set; }

            public Guid Contractor { get; set; }

            public Guid Batch { get; set; }

            public Guid Contract { get; set; }

            public Guid CreatedBy { get; set; }

            public DateTime CreatedDate { get; set; }
        }

        static void ApplyDealData(TypedRecord data, Deal target, IRegistry registry)
        {
            target.Id = data.GetGuid("Id");
            target.Actor = data.GetGuid("Actor");
            target.Batch = data.GetGuid("Batch");
            target.Contract = data.GetGuid("Contract");
            target.Contractor = data.GetGuid("Contractor");
            target.Cost = data.GetDecimal("Cost");
            target.CreatedBy = data.GetGuid("CreatedBy");
            target.CreatedDate = data.GetDateTime("CreatedDate");
            target.Date = data.GetDateTime("Date");
            target.Description = data.GetString("Description");
            target.Item = data.GetGuid("Item");
            target.Quantity = data.GetDecimal("Quantity");
            target.SourcePlace = data.GetGuid("SourcePlace");
            target.TargetPlace = data.GetGuid("TargetPlace");
        }

        static object GetDealData(Deal instance, string name, IRegistry registry)
        {
            if (StringComparer.OrdinalIgnoreCase.Equals("orig__Id", name))
                return registry.GetIdentifier<Deal, Guid>(instance);
            if (StringComparer.OrdinalIgnoreCase.Equals("Id", name))
                return instance.Id;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Actor"))
                return instance.Actor;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Batch"))
                return instance.Batch;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Contract"))
                return instance.Contract;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Contractor"))
                return instance.Contractor;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Cost"))
                return instance.Cost;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "CreatedBy"))
                return instance.CreatedBy;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "CreatedDate"))
                return instance.CreatedDate;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Date"))
                return instance.Date;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Description"))
                return instance.Description;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Item"))
                return instance.Item;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "Quantity"))
                return instance.Quantity;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "SourcePlace"))
                return instance.SourcePlace;
            if (StringComparer.OrdinalIgnoreCase.Equals(name, "TargetPlace"))
                return instance.TargetPlace;
            return null;
        }

        static IComponentConfiguration<ICommandSet> CreateDealCommandSet()
        {
            return Component.CommandSet.FromTable(
                Component.CreateTable("Deal")
                    .Key<Guid>("Id")
                    .Column<String>("Description")
                    .Column<DateTime>("Date")
                    .Column<Guid>("Item")
                    .Column<decimal>("Quantity")
                    .Column<Decimal>("Cost")
                    .Column<Guid>("SourcePlace")
                    .Column<Guid>("TargetPlace")
                    .Column<Guid>("Actor")
                    .Column<Guid>("Contractor")
                    .Column<Guid>("Batch")
                    .Column<Guid>("Contract")
                    .Column<Guid>("CreatedBy")
                    .Column<DateTime>("CreatedDate"), false);

        }

        static OverStoreConfiguration CreateDealConfig(string connectionString)
        {
            return OverStoreFluent.Configure()
                .LogToConsole().ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                .CustomClassRepository<Deal, Guid>()
                    .WithDepot(Component.Depot.Delegate<Deal, Guid>(
                                    () => new Deal(),
                                    (instance, identifier) => instance.Id = identifier,
                                    Component.IdentifierGenerator.Guid<Deal>()))
                    .WithMapper(Component.Mapper.Delegate<Deal>(GetDealData, ApplyDealData))
                    .WithCommands(CreateDealCommandSet())
                    .WithChangesTracker(Component.InstanceChangesTracker.Delegate<Deal, Guid, int>(item =>
                        {
                            unchecked
                            {
                                return item.Actor.GetHashCode() + item.Batch.GetHashCode() + item.Contract.GetHashCode() +
                                       item.Contractor.GetHashCode() + item.Cost.GetHashCode() + item.Date.GetHashCode() +
                                       (item.Description ?? String.Empty).GetHashCode() + item.Item.GetHashCode() +
                                       item.Quantity.GetHashCode() + item.SourcePlace.GetHashCode() + item.TargetPlace.GetHashCode();
                            }
                        }))
                    .ByIdentifierAttaching(i => i.Id)
                    .ParameterlessReader(true)
                        .ReadCommand()
                            .UseSqlCommand("select * from Deal")
                        .EndCommand()
                        .WithIdentifierProvider(Component.IdentifierProvider.Delegate<Guid>(r => r.GetGuid("Id")))
                    .EndReader()
                .EndObjectRepository()
            .End();
        }

        static OverStoreConfiguration CreateDealConfigWithDummyChangesTracker(string connectionString)
        {
            return OverStoreFluent.Configure()
                       .LogToConsole().ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                       .CustomClassRepository<Deal, Guid>()
                            .WithDepot(new DelegateDepotConfiguration<Deal, Guid>
                                        {
                                            StoreIdentifierCopy = (instance, id) => instance.Id = id,
                                            CreateInstanceFunction = () => new Deal(),
                                            IdentifierGenerator = Component.IdentifierGenerator.Guid<Deal>()
                                        })
                            .WithMapper(Component.Mapper.Delegate<Deal>(GetDealData, ApplyDealData))
                            .WithCommands(CreateDealCommandSet())
                            .ByIdentifierAttaching(i => i.Id)
                            .ParameterlessReader(true)
                                .WithCommand(Component.AdoCommand.ReadCommand("select * from Deal"))
                                .WithIdentifierProvider(Component.IdentifierProvider.Delegate<Guid>(r => r.GetGuid("Id")))
                            .EndReader()
                        .EndObjectRepository()
                .End();
        }

        [TestMethod]
        public void ModifyFewDeals()
        {
            var connectionString = TestHelper.CreateMyMoneyCopyAndBuildConnectionString();
            using (var session = CreateDealConfig(connectionString).CreateSession())
            {
                var deals = session.CreateReader<Deal>().ToList();

                deals.Skip(30).First().Description = "Modified deal";
                deals.Skip(150).First().Description = "Important deal";
                deals.Skip(300).First().Description = "Futures";

                session.SaveAll();
            }
        }

        [TestMethod]
        public void ReadDealsWithRegisteringChanges()
        {
            var connectionString = TestHelper.CreateMyMoneyCopyAndBuildConnectionString();
            using (var session = CreateDealConfig(connectionString).CreateSession())
            {
                var deals = session.CreateReader<Deal>().ToList();
            }
        }

        [TestMethod]
        public void ReadDealsWithoutRegisteringChanges()
        {
            var connectionString = TestHelper.CreateMyMoneyCopyAndBuildConnectionString();
            using (var session = CreateDealConfigWithDummyChangesTracker(connectionString).CreateSession())
            {
                var deals = session.CreateReader<Deal>().ToList();
            }
        }
    }
}
